// tree class for NBody Simulation

#include "octree_class.h"
using namespace std;

/*------------------------------------
name:		default constructor
purpose:	construct empty tree
in:			none
out:		none
------------------------------------*/
octant::octant()
{
	mass_total = 0;
	particle_total = 0;
	length = 1.0;
	dimensions[x_back] = -length;
	dimensions[x_front] = length;
	dimensions[y_back] = -length;
	dimensions[y_front] = length;
	dimensions[z_back] = -length;
	dimensions[z_front] = length;
	center[X] = 0.0;
	center[Y] = 0.0;
	center[Z] = 0.0;
	particle = NULL;
	for ( int i (0); i < 8; i++ ) children[i] = NULL;
}

/*------------------------------------
name:		constructor
purpose:	construct tree from input
in:			double
out:		none
------------------------------------*/
octant::octant( vector<double> parent, double l, double w, double h )
{
	mass_total = 0;
	particle_total = 0;
	dimensions[x_back] = parent[X];
	dimensions[x_front] = parent[X] + l;
	dimensions[y_back] = parent[Y];
	dimensions[y_front] = parent[Y] + w;
	dimensions[z_back] = parent[Z];
	dimensions[z_front] = parent[Z] + h;
	length = (x_front - x_back)/2.0;
	center[X] = (x_back + x_front)/2.0;
	center[Y] = (y_back + y_front)/2.0;
	center[Z] = (z_back + z_front)/2.0;
	particle = NULL;
	for ( int i (0); i < 8; i++ ) children[i] = NULL;
}
	
/*------------------------------------
name:		default deconstructor
purpose:	deconstruct tree
in:			none
out:		none
------------------------------------*/
octant::~octant()
{
	// destroy parent then children
}

/*------------------------------------
name:		add
purpose:	add particle to tree
in:			pointer
out:		none
------------------------------------*/
void octant::add( Body * p )
{
	mass_total += p.mass;
	particle_total += 1;
	if( particle_total > 1 )
	{
		check_pos( p ).add( p );
		check_pos( particle ).add( particle );
		particle = NULL;
	}
	else
		particle = p;
}

/*------------------------------------
name:		check_pos
purpose:	check particle position
in:			vec, vec
out:		none
------------------------------------*/
octant octant::check_pos( Body * p )
{
	double l(length), w(length), h(length);
	if( center[X] < p.pos[X] )
	{
		if( center[Y] < p.pos[Y] )
		{
			if( center[Z] < p.pos[Z] )
			{
				children[0] = new octant( center, l, w, h );
				return children[0];
			}
			else if( p.pos[Z] < center[Z] )
			{
				children[4] = new octant( center, l, w, -1*h );
				return children[4];
			}
		}
		else if( p.pos[Y] < center[Y] )
		{
			if( center[Z] < p.pos[Z] )
			{
				children[1] = new octant( center, l, -1*w, h );
				return children[1];
			}
			else if( p.pos[Z] < center[Z] )
			{
				children[5] = new octant( center, l, -1*w, -1*h );
				return children[5];
			}
		}
	}
	else if( p.pos[X] < center[X] )
	{
		if( center[Y] < p.pos[Y] )
		{
			if( center[Z] < p.pos[Z] )
			{
				children[3] = new octant( center, -1*l, w, h );
				return children[3];
			}
			else if( p.pos[Z] < center[Z] )
			{
				children[7] = new octant( center, -1*l, w, -1*h );
				return children[7];
			}
		}
		else if( p.pos[Y] < center[Y] )
		{
			if( center[Z] < p.pos[Z] )
			{
				children[2] = new octant( center, -1*l, -1*w, h );
				return children[2];
			}
			else if( p.pos[Z] < center[Z] )
			{
				children[6] = new octant( center, -1*l, -1*w, -1*h );
				return children[6];
			}
		}
	}
}
	
/*------------------------------------
name:		find
purpose:	find particle
in:			int
out:		pointer
------------------------------------*/
Body * octant::find( int n )
{
	int counter(0);
	for( int i(0); i < 8; i++ )
	{
		if( children[i] != NULL ) 
		{
			children[i].find( n );
			if( children[i].particle != NULL ) counter++;
			if( counter == n ) return children[i].particle;
		}
	}
}

/*------------------------------------
name:		force
purpose:	calculate force from box
in:			
out:		double
------------------------------------*/